#pragma once

/* socket封装 */

#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <strings.h>

#include "log.hpp"
#include "err.hpp"

const int g_backlog = 32; // 全连接队列长度

class Sock
{
public:
    static int Socket()
    {
        // 1. 创建套接字
        int sockfd = socket(AF_INET, SOCK_STREAM, 0); // TCP通信
        if (sockfd == -1)
        {
            logMessage(FATAL, "create socket error");
            exit(SOCKET_ERROR);
        }
        logMessage(NORMAL, "create socket success: %d", sockfd);

        // 设置地址复用
        int opt = 1;
        setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

        return sockfd;
    }

    static void Bind(int sockfd, int port)
    {
        // 2. bind网络信息
        struct sockaddr_in local;
        bzero(&local, 0);

        local.sin_family = AF_INET;
        local.sin_addr.s_addr = INADDR_ANY;
        local.sin_port = htons(port);

        int n = bind(sockfd, (struct sockaddr *)&local, sizeof(local));
        if (n == -1)
        {
            logMessage(FATAL, "bind error");
            exit(BIND_ERROR);
        }
        logMessage(NORMAL, "bind success");
    }
    static void Listen(int sockfd)
    {
        // 3. TCP面向连接——通信前先建立连接——设置socket为监听状态
        int n = listen(sockfd, g_backlog); // TODO
        if (n == -1)
        {
            logMessage(FATAL, "listen socket error");
            exit(LISTEN_ERROR);
        }
        logMessage(NORMAL, "listen socket success");
    }

    static int Accept(int listen_sockfd, std::string* clientip, uint16_t* clientport)
    {
        // 4. 获取新连接
        struct sockaddr_in from;
        bzero(&from, 0);
        socklen_t fromlen = sizeof(from);

        // sockfd，未来和客户端进行通信的套接字fd
        int sockfd = accept(listen_sockfd, (struct sockaddr *)&from, &fromlen);
        if (sockfd == -1) logMessage(ERROR, "accept error, to do next accept");
        else
        {
            logMessage(NORMAL, "accept a new link success, get new socket: %d", sockfd); // TODO
            // 提取客户端的ip和port
            *clientip = inet_ntoa(from.sin_addr);
            *clientport = ntohs(from.sin_port);
        }

        return sockfd;
    }
};
